1 module hip.wasm; 2 version(WebAssembly): 3 4 ///WebAssembly.Table replacement for HipremeEngine 5 private __gshared ubyte* function(ubyte* args)[] _annonymousFunctionTable; 6 ///JSFunctions are represented opaquely right now. 7 alias JSFunction(T) = ubyte*; 8 9 ///Gets a unique function index for usage in the table 10 extern(C) size_t _getFuncAddress(ubyte* fn); 11 12 ///Javascript function to call a D callback. 13 export extern(C) ubyte* __callDFunction(size_t addr, ubyte* args) 14 { 15 return _annonymousFunctionTable[addr](args); 16 } 17 18 ///Checks if function has been called with required arguments. 19 private ubyte* validateArguments(alias fn)(ubyte* args) 20 { 21 import std.traits; 22 //Only checking the count of 23 assert(Parameters!(fn).length <= *cast(size_t*)args, 24 fn.stringof~"Expected "~Parameters!(fn).length.stringof~" parameters"); 25 return args + size_t.sizeof; //Only uses 1 size_t to determine arguments validity 26 } 27 28 29 struct Arguments(alias Func) 30 { 31 import std.traits; 32 Parameters!Func params; 33 } 34 35 Struct loadMemoryInStruct(Struct)(ubyte* arg) 36 { 37 Struct ret; 38 size_t last = 0; 39 foreach(ref v; ret.tupleof) 40 { 41 static if(is(typeof(v) : string)) 42 { 43 { 44 ubyte* data = arg+last; 45 size_t length = *cast(size_t*)data; 46 v = cast(string)data[size_t.sizeof..length+size_t.sizeof]; 47 } 48 } 49 else static if(is(typeof(v) : ubyte[])) 50 { 51 { 52 ubyte* data = arg+last; 53 size_t length = *cast(size_t*)data; 54 v = cast(ubyte[])data[size_t.sizeof..length+size_t.sizeof]; 55 } 56 } 57 else 58 memcpy(&v, arg+last, v.sizeof); 59 last+= v.sizeof; 60 } 61 return ret; 62 } 63 64 Arguments!fn wasmParametersFromUbyte(alias fn)(ubyte* arg) 65 { 66 return loadMemoryInStruct!(Arguments!fn)(arg); 67 } 68 69 /** 70 * Whenever wanting to pass a callback to Javascript, call this function instead. 71 * This function is not expected to meet usercode. But it will stay here nevertheless. 72 */ 73 ubyte* sendJSFunction(alias fn)() 74 { 75 import std.traits; 76 static ubyte* function(ubyte* arg) convertedFunc = (ubyte* arg) 77 { 78 Arguments!fn params = wasmParametersFromUbyte!fn(validateArguments!fn(arg)); 79 static if(!is(ReturnType!fn == void)) 80 return fn(params.tupleof); 81 else 82 { 83 fn(params.tupleof); 84 return null; 85 } 86 }; 87 size_t addr = _getFuncAddress(cast(ubyte*)fn); 88 if(addr >= _annonymousFunctionTable.length) _annonymousFunctionTable.length = addr+1; 89 _annonymousFunctionTable[addr] = convertedFunc; 90 91 return cast(ubyte*)fn; 92 } 93 94 95 struct JSDelegate 96 { 97 ubyte* funcHandle; 98 ubyte* funcptr; 99 ubyte* ctx; 100 } 101 102 alias JSStringType = AliasSeq!(size_t, void*); 103 104 struct JSString 105 { 106 size_t length; 107 void* ptr; 108 this(string str) 109 { 110 length = str.length; 111 ptr = cast(void*)str.ptr; 112 } 113 } 114 115 alias JSDelegateType(T) = AliasSeq!(ubyte*, ubyte*, ubyte*); 116 117 JSDelegate sendJSDelegate(alias dg)() 118 { 119 import std.traits; 120 auto convertedFunc = toFunc!dg; 121 size_t addr = _getFuncAddress(cast(ubyte*)convertedFunc); 122 if(addr >= _annonymousFunctionTable.length) _annonymousFunctionTable.length = addr+1; 123 _annonymousFunctionTable[addr] = convertedFunc; 124 125 126 return JSDelegate(cast(ubyte*)addr, cast(ubyte*)dg.funcptr, cast(ubyte*)dg.ptr); 127 } 128 129 130 131 /** 132 * Generates a delegate which adds the `this` context from the arguments. 133 * It also packs the arguments sent from Javascript and transform them to the 134 * data the D delegate expects. There is also a validation in the arguments received. 135 */ 136 ubyte* function(ubyte* args) toFunc(alias dg)() 137 { 138 import std.traits; 139 import hip.wasm; 140 alias Params = Parameters!dg; 141 alias DgArgs = Arguments!dg; 142 enum Length = DgArgs.tupleof.length; 143 alias Ret = ReturnType!dg; 144 145 static ubyte* function(ubyte* arg) ret = (ubyte* arg) 146 { 147 size_t argsCount = *cast(size_t*)arg; 148 assert(argsCount >= 2, "D delegates expects at least 2 arguments [Function Pointer, Function Context]"); 149 assert(argsCount - 2 <= Length, "Expected "~Length.stringof~" parameters."); 150 size_t[3] baseArgs = (cast(size_t*)arg)[0..3]; 151 152 static DgArgs delegateArguments; 153 static if(Length > 0) 154 delegateArguments = loadMemoryInStruct!DgArgs(arg + size_t.sizeof*3); 155 156 static if(Length > 0) 157 { 158 static if(is(Ret == void)) 159 void delegate(Params) dg; 160 else 161 ubyte* delegate(Params) dg; 162 } 163 else 164 { 165 static if(is(Ret == void)) 166 void delegate() dg; 167 else 168 ubyte* delegate() dg; 169 } 170 171 dg.funcptr = cast(typeof(dg.funcptr))baseArgs[1]; 172 dg.ptr = cast(void*)baseArgs[2]; 173 174 static if(Length > 0) 175 { 176 static if(is(Ret == void)) 177 { 178 dg(delegateArguments.tupleof); 179 return null; 180 } else return dg(delegateArguments.tupleof); 181 } 182 else 183 { 184 static if(is(Ret == void)) 185 { 186 dg(); 187 return null; 188 } else return dg(); 189 } 190 }; 191 return ret; 192 } 193 194 195 196 ubyte[] getWasmBinary(ubyte* input) 197 { 198 size_t length = *cast(size_t*)input; 199 ubyte[] ret = (input+size_t.sizeof)[0..length]; 200 return ret; 201 } 202 203 void freeWasmBinary(ubyte[] binary) 204 { 205 import core.memory; 206 ubyte* ptr = binary.ptr - size_t.sizeof; 207 GC.free(ptr); 208 }